﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.ComponentModel;

namespace PersonaLibrary.Search
{
    public class CollectionSearchProperty<T> : BaseSearchProperty
        where T : class
    {
        private MemberExpression _collectionItemProperty;
        private readonly MethodInfo _info;
        private readonly ParameterExpression _itemP;

        public CollectionSearchProperty(ParameterExpression parametr)
            : base(parametr)
        {
            _info = typeof (Enumerable).GetMethods().Single(x => x.Name == "Any" && x.GetParameters().Length == 2);
            _info = _info.MakeGenericMethod(typeof(T));
            _itemP = Expression.Parameter(typeof(T), "item");
        }

        public string CollectionItemProperty
        {
            set
            {
                OnCollectionItemNameChanged(value);
            }
        }

        private void OnCollectionItemNameChanged(string value)
        {
            MemberExpression property = null;
            foreach (var propName in value.Split(".".ToCharArray()))
            {
                if (property == null)
                {
                    property = Expression.Property(_itemP, propName);
                }
                else
                    property = Expression.Property(property, propName);
            }
            _collectionItemProperty = property;
        }

        protected override List<string> CreateUnaryOperators()
        {
            return new List<string>
                       {
                           "Содержит",
                           "Несодержит",
                           "Начинается",
                           "Заканчивается"
                       };
        }

        public override Expression CreateUnary()
        {
            Expression result = null;

            switch (SelectedUnaryOperator)
            {
                case "Содержит":
                    result = Property.NotNullTesting(
                            Expression.Call(null, _info,  
                                Property,
                                Expression.Lambda(Expression.Call(ResetPropertyRegistry(), typeof(string).GetMethod("Contains", new[] { typeof(string) }),
                                    ResetConstRegistry()), _itemP)
                            ));
                    break;
                case "Несодержит":
                    result = Property.NotNullTesting(
                        Expression.Not(Expression.Call(null, _info,
                            Property,
                            Expression.Lambda(Expression.Call(ResetPropertyRegistry(), typeof(string).GetMethod("Contains", new[] { typeof(string) }), 
                            ResetConstRegistry()), _itemP))));
                    break;
                case "Начинается":
                    result = Property.NotNullTesting(
                        Expression.Call(null, _info,
                            Property,
                            Expression.Lambda(Expression.Call(ResetPropertyRegistry(), typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
                            ResetConstRegistry()), _itemP)));
                    break;
                case "Заканчивается":
                    result = Property.NotNullTesting(
                        Expression.Call(null, _info,
                            Property,
                            Expression.Lambda(Expression.Call(ResetPropertyRegistry(), typeof(string).GetMethod("EndsWith", new[] { typeof(string) }),
                            ResetConstRegistry()), _itemP)));
                    break;
            }
            return result;
        }

        private Expression ResetPropertyRegistry()
        {
            Expression tmpExpression = Expression.Call(typeof(Convert).GetMethod("ToString", new[] { typeof(object) }), _collectionItemProperty);
            return Expression.Call(tmpExpression, typeof(string).GetMethod("ToUpper", new Type[] { }));
        }

        private Expression ResetConstRegistry()
        {
            return Expression.Call(Constant, typeof(string).GetMethod("ToUpper", new Type[] { }));
        }
    }
}
